perm filename INTERP.PAL[HAL,HE]18 blob
sn#199574 filedate 1976-02-04 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00027 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00004 00002 .SBTTL Interpreter Data structures
C00010 00003 INTINIT, NEWENV, MINTS
C00013 00004 Interpreter itself: INTERP
C00019 00005 GETARG, GETSCA, GETVEC, GETTRN
C00023 00006 Variable declaration: MVAR, KVAR
C00026 00007 Stack ops: GTVAL, IGTVAL, CHNGE, ICHNGE, PUSH, POP, COPY, REPLACE, FLUSH
C00031 00008 Global reference routines GLBLNK, GLOBSR.
C00036 00009 Flow-of-control: PROC, RETURN
C00042 00010 FORCHK, JUMP, JUMPC
C00045 00011 SPAWN, SPROUT, TERMINATE
C00053 00012 Calculator routines: MEXP, MCLC, DCLC, ENDCLC
C00058 00013 Changer routines: MCHGR, GTOLD, GTNEW
C00061 00014 return scalars: SADD, SSUB, SMUL, SDIV, SNEG, VDOT, PVDOT, VMAG, SSBRTN
C00068 00015 Vector utilities: UNITV, CROSV
C00074 00016 Return vectors: SVMUL, TVMUL, VMAKE, VADD
C00078 00017 Return a trans: TMAKE, TVADD, TTMUL, TINVRT
C00086 00018 Motion: MOVE, CENTER, STOP, WHERE
C00093 00019 Condition monitors: CMMAK
C00099 00020 CMENBL, CMDSBL, CMDEST, CMTRIG, CMSKED, CMUNCR
C00106 00021 Force condition monitors. Data structures. TABOFS, FMBLK, MAKFORCE, DESFORCE
C00114 00022 GETFORCE, MAKRT
C00122 00023 Events: MAKEVT, SIGNAL, WAITE, DESEVT
C00127 00024 Output routines: PRINT, VALPRN, VARPRN, TYPVAL
C00131 00025 BREAK, NOOP, TOPAL
C00133 00026 Initialization psops: PROG, ENDP, NOTICE
C00139 00027 BUGS
C00140 ENDMK
C⊗;
.SBTTL Interpreter ;Data structures
COMMENT ⊗
Register uses in the interpreter:
R5 used by some routines as the display register
R4 points to interpreter status block
R3 interpreter stack pointer
R2 not used by the main interpreter loop. Can be munged by
any primary interpreter routine.
Each interpreter has a stack which it uses to store pointers to
currently "open" variables. During the course of a calculation,
operands and temporary result cells will be open in this fashion.
The "interpreter stack" is pointed to by R3. When a new interpreter
is sprouted, it is given a new stack area. Each interpreter has
certain status information which facilitates transfer of control
between interpreters. This information is kept in the interpreter
status block, which is always pointed to by R4. Most important are
the IPC, the Interpreter Program Counter, the ENV, which points to
the local environment, and LEV, which stores the current lexical
level.
Each procedure has an environment, which is a data area holding
information vital to that procedure. This includes pointers to all
the variables local to that procedure, and return information.
The environments are administered under the small block allocator
with garbage collection.
⊗
INSTSZ == 20 ;Size of an interpreter stack
;Interpreter status block
II == 0
XX IPC ;Interpreter program counter. Leave this as first field!
XX NXTINT ;Next interpreter in the list. For GC of the stacks.
XX STKBAS ;Location of start of stack area. Needed
;for eventual reclamation.
XX ENV ;Location of local environment
XX LEV ;Lexical level of current execution
XX STA ;Status bits for condition codes: 0 means all well.
XX PCB ;Location of process control block (for reclamation)
XX EVT ;The event to signal as this interpreter goes away
XX CMCB ;Pointer to c-m control block if this is a checker or a body
XX OLDV ;The "old value" used by changers
XX NEWV ;The "new value" used by changers
.IFNZ ALAID ;Special debugging information
XX INTNAM ;Name of the interpreter
XX INTMA1 ; two words
XX DEBMOD ;The mode bits for debugging.
ALDSS == 1 ;1 => Single step mode
ASDTE == 2 ;1 => Terminate this interpeter
XX WAKEVT ;Event to wait on during halts
.ENDC
ISBS == II/2 ;Size (in words) of interpreter status block
;Fixed fields in the environment of each process
II == 0
XX SLINK ;Pointer to environment of next (outer, lower
; numbered) block
XX OLEV ;Old level. The lexical level of calling process.
XX OENV ;Old environment, the one for the calling process.
XX OIPC ;Old IPC. Program counter for calling process.
XX LVARS ;First location where pointers to local variables go
; Mechanism bits.
YARM == 1
YHAND == 2
BARM == 4
BHAND == 10
ANARM == YARM + BARM
AHAND == YHAND + BHAND
; Table offsets for various mechanisms.
OFYARM == 0
OFYHAND == 6*2
OFBARM == 7*2
OFBHAND == 15*2
; Environment offsets for the various mechanisms
YAOFST == 10
YHOFST == 12
BAOFST == 14
BHOFST == 16
; Environment offsets for the calculators of those mechanisms
YACOFS == 20
YHCOFS == 22
BACOFS == 10 ; Will be 24 when have yellow arm
BHCOFS == 12 ; Will be 26 when have yellow arm
;INTINIT, NEWENV, MINTS
INTEVT: 0 ;The event that interlocks references to ISTBLK.
GLBEVT: 0 ;The event that interlocks references to GLBTAB.
INTINIT: ;Initializes the above events
EVMAK ;Initialize the INTEVT.
MOV (SP),INTEVT;
EVSIG ;
EVMAK ;Initialize the GLBEVT.
MOV (SP),GLBEVT ;
EVSIG ;
MOV #GLBTAB,GLBEND ;Initialize GLBEND. This wipes out all globals.
RTS PC ;Done
MINTS: ;Marking method for interpeters
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
EVWAIT INTEVT ;Enter critical region
MOV NXTINT+ISTBLK,R2 ;R2 ← LOC[first real interpeter status block]
BEQ MINTS1 ;If none, then done
;mark the stack
MINTS2: MOV STKBAS(R2),R3 ;R3 ← LOC[interpreter stack base]
ADD #2*INSTSZ,R3 ;R3 ← LOC[verge of new stack] (INSTSZ is in bytes)
MINTS4: MOV -(R3),R0 ;R0 ← stack entry
BEQ MINTS6 ;If 0, then end of stack (RF: this wont work!!)
JSR PC,MARKQ ;
MOV R0,(R3) ;Put it back (compactification may move it)
BR MINTS4 ;
;mark the environments
MINTS6: MOV ENV(R2),R3 ;R3 ← environment
MOV R3,R0 ;
JSR PC,MARKQ ;
MOV R0,ENV(R2) ;
MINTS5: MOV SLINK(R3),R0 ;R0 ← next environment
BNE MINTS3 ;if any
JSR PC,MARKQ ;
MOV R0,SLINK(R3) ;
MOV R0,R3 ;
BR MINTS5 ;
MINTS3: MOV NXTINT(R2),R2 ;R2 ← LOC[next interpreter status block]
BNE MINTS2 ;Repeat as necessary
MINTS1: MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
EVSIG INTEVT ;
RTS RF ;Return
NEWENV: ;Gets a new environment, returns address in R0.
.IFNZ SMALLB
MOV #ENVSPC,R0 ;
JMP GETSBK ;Allocate from small blocks
.IFF
MOV #ENVSIZ,R0 ;
JMP GTFREE ;Allocate from large blocks
.ENDC
;Interpreter itself: INTERP
.MACRO MAKEOP CNAME, ANAME ;Compiler name, Address name
XX CNAME
ANAME
.ENDM
;The interpreter operation table
INTOPS: MAKEOP XINVALID,INVALID ;Illegal instruction
.INSRT INTOPS.PAL[HAL,HE]
INSEND = II ;Marks the end of the instructions
.MACRO BMPIPC ;
ADD #2,IPC(R4) ;Bump IPC
.ENDM ;
.MACRO BACKIPC ;
SUB #2,IPC(R4) ;Backup IPC
.ENDM ;
.MACRO CCC ;Clear condition code
; CLR R0 ;Clear condition code. Not used right now.
.ENDM
.MACRO SCC ;Set condition code
; MOV #2,R0 ;Set condition code. Not used right now.
.ENDM
.IFZ ALAID ;The ALAID version is in ALAID.PAL
INTERP:
MOV R3,R0 ;Save the limit of the interpreter stack for error checking.
SUB #INSTSZ-2,R0
MOV R0,-(SP) ;
INT1: CMP R3,(SP) ;Interpreter stack overflow?
BGE INT3 ;No. Go to next instruction.
HALERR INTMS3 ;Yes. Complain.
INT3: MOV @IPC(R4),R0 ;R0 ← next instruction
BLE INVALID ;Instruction out of range
CMP R0,#INSEND ;Is instruction too large?
BLE INT2 ;No.
INVALID:HALERR INTMS1 ;Yes. complain.
INT2: BMPIPC ;Bump IPC
JSR PC,@INTOPS(R0) ;Call the appropriate routine
BR INT1 ;Repeat interpreter loop
INTMS1: ASCIE /INTERPRETER INSTRUCTION OUT OF RANGE/
INTMS2: ASCIE /INTERPRETED INSTRUCTION RETURNED FAILURE/
INTMS3: ASCIE /INTERPRETER STACK OVERFLOW/
.ENDC
; GETARG, GETSCA, GETVEC, GETTRN
GETARG:
COMMENT ⊗
Arguments:
R0=variable name: high byte is lexical level, low byte is offset.
R4=pointer to interpreter status block.
Result:
R0← pointer to address of desired variable.
R1 clobbered.
This routine returns in R0 a pointer to the location in the current
environment (or, if necessary, more global environment) which
points to the variable which is named in R0. ⊗
MOV R2,-(SP) ;Save R2
MOVB R0,R1 ;R1 ← Offset desired
CLRB R0 ;
SWAB R0 ;R0 ← Lexical level
MOV ENV(R4),R2 ;R2 ← LOC[local environment]
SUB LEV(R4),R0 ;R0 ← Difference in levels: desired-got
BEQ GTRG1 ;Diff=0; can use R2 as pointer at right base.
BHI GTERR ;If diff>0, then value inaccessible.
GTRG2: MOV SLINK(R2),R2;Must go up a level. R2 ← LOC[more global environment]
INC R0 ;R0 ← New difference in levels
BNE GTRG2 ;If not yet good, then move up another level
GTRG1: ADD R2,R1 ;R1 ← environment + offset = location of desired pointer
MOV (SP)+,R2 ;Restore R2.
MOV R1,R0 ;
RTS PC ;Done.
GTERR: HALERR GTMS1
GTMS1: ASCIE /ATTEMPT TO ACCESS UNAVAILABLE VARIABLE/
GETSCA: ;Gets place for a scalar result, and places a pointer on
;the interpreter stack. Location is returned in R0.
;Simple procedure.
.IFNZ SMALLB
MOV #SCASPC,R0 ;
JSR PC,GETSBK ;Allocate from small blocks
.IFF
MOV #2,R0 ;Number of words needed
JSR PC,GTFREE ;R0 ← LOC[new block]
.ENDC
MOV R0,-(R3) ;Push new value cell pointer on interpreter stack.
RTS PC ;Done
GETVEC: ;Gets place for a vector result, and places a pointer on
;the interpreter stack. Location is returned in R0.
;Simple procedure.
.IFNZ SMALLB
MOV #VCTSPC,R0 ;
JSR PC,GETSBK ;Allocate from small blocks
.IFF
MOV #10,R0 ;Number of words needed
JSR PC,GTFREE ;R0 ← LOC[new block]
.ENDC
MOV R0,-(R3) ;Push new value cell pointer on interpreter stack.
RTS PC ;Done
GETTRN: ;Gets place for a trans result, and places a pointer on
;the interpreter stack. Location is returned in R0.
;Simple procedure.
.IFNZ SMALLB
MOV #TRNSPC,R0 ;
JSR PC,GETSBK ;Allocate from small blocks
.IFF
MOV #40,R0 ;Number of words needed
JSR PC,GTFREE ;R0 ← LOC[new block]
.ENDC
MOV R0,-(R3) ;Push new value cell pointer on interpreter stack.
RTS PC ;Done
;Variable declaration: MVAR, KVAR;
MVAR:
COMMENT ⊗ A list of arguments, each of which is an offset. This list
is terminated by a zero entry. For each argument, a fresh graph node
is created (with no value) and a pointer to it is placed in the
environment at the desired offset, current level. ⊗
MOV @IPC(R4),-(SP) ;push offset
BIC #177400,(SP);Get rid of level info.
BEQ MVAR1 ;If none, done
BMPIPC ;Bump IPC
CLR R0 ;The new graph node should get no value cell.
JSR PC,MAKEVN ;R0 ← LOC[a new variable node]
ADD ENV(R4),(SP);stack pointer into environment
MOV R0,@(SP)+ ;Point the environment to the graph node
BR MVAR ;Repeat
MVAR1: TST (SP)+ ;Clean off stack
BMPIPC ;Bump IPC
CCC ;Clear condition code.
RTS PC ;Done
KVAR:
COMMENT ⊗ A list of arguments, each of which is an offset. This list
is terminated by a zero entry. For each argument, the corresponding
graph node is destroyed in the current environment. Any function in
the graph structure is thereby released. (Attempt is made to
validate any dependents first.) ⊗
MOV @IPC(R4),R2 ;R2 ← offset
BIC #177400,R2 ;Get rid of level info.
BEQ KVAR1 ;If none, done
BMPIPC ;Bump IPC
ADD ENV(R4),R2 ;R2 ← LOC[pointer at graph node]
MOV (R2),R0 ;R0 ← LOC[graph node]
JSR PC,DELVN ;Get this guy deleted
CLR (R2) ;Remove the pointer in the environment
BR KVAR ;Repeat
KVAR1: BMPIPC ;Bump IPC
CCC ;Clear condition code
RTS PC ;Done
;Stack ops: GTVAL, IGTVAL, CHNGE, ICHNGE, PUSH, POP, COPY, REPLACE, FLUSH
GTVAL:
COMMENT ⊗ The argument is a level-offset pair. The variable
referenced by that pair is examined and a pointer to its value cell
is placed on the stack. ⊗
MOV @IPC(R4),R0 ;Pick up level-offset name of argument
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[desired graph node]]
MOV (R0),R0 ;R0 ← LOC[desired graph node]
BEQ GTVL2 ;But if 0, then bug
GTVL4: CALL GETVAL,<R0>;R0 ← value
GTVL3: MOV R0,-(R3) ;Push value on interpreter stack.
BEQ GTVL1 ;But if 0, then bug
CCC ;Clear condition code.
RTS PC ;Done
GTVL1: HALERR GTVMS1 ;Complain
SCC ;Set condition code
RTS PC ;Done
GTVL2: HALERR GTVMS2 ;Complain
BR GTVL3 ;But comply
GTVMS1: ASCIE </GTVAL FOUND A NULL VALUE. MAY CONTINUE/>
GTVMS2: ASCIE </GTVAL FOUND A NULL GRAPH NODE. MAY CONTINUE/>
IGTVAL:
COMMENT ⊗ Immediate version of GTVAL. The argument points directly
to the graph node whose value is desired. A pointer to the value
cell is placed on the stack. ⊗
MOV @IPC(R4),R0 ;R0 ← LOC[desired graph node]
BMPIPC ;Bump IPC
CALL GETVAL,<R0>;R0 ← value
MOV R0,-(R3) ;Push value on interpreter stack.
CCC ;Clear condition code.
RTS PC ;Done
CHNGE:
COMMENT ⊗ Pops the value from top of stack into the graph structure
pointed to by the level-offset pair given in the argument. ⊗
MOV @IPC(R4),R0 ;Pick up level-offset name of argument
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[Desired graph node]]
MOV (R0),R0 ;R0 ← LOC[Desired graph node]
BEQ CHNGE1 ;If any
CHNGE2: CALL CHANGE,<R0,(R3)>
POP: TST (R3)+ ;Pop stack
CCC ;Clear condition code.
RTS PC ;Done
CHNGE1: HALERR CHNMES ;Complain
TST (R3)+ ;Get rid of the value
SCC ;Set condition code
RTS PC ;Done
CHNMES: ASCIE </CAN'T ASSIGN INTO UNINITIALIZED VARIABLE/>
ICHNGE:
COMMENT ⊗ Immediate version of CHNGE. Pops the value from top of
stack into the graph structure pointed to directly by the argument. ⊗
MOV @IPC(R4),R0 ;R0 ← LOC[desired graph node]
BMPIPC ;Bump IPC
CALL CHANGE,<R0,(R3)>
TST (R3)+ ;Pop stack
CCC ;Clear condition code.
RTS PC ;Done
PUSH: MOV @IPC(R4),-(R3);Put argument directly on stack
BMPIPC ;Bump IPC
CCC ;Clear condition code.
RTS PC ;Done
; Interpreter routine. Copies the nth element in stack to the top,
; where the curent top is 0.
COPY: MOV @IPC(R4),R0 ;Pick up argument.
BMPIPC ;Bump IPC
ADD R0,R0 ;Double R0 to make it in bytes
ADD R3,R0 ;R0 ← LOC[stack element to be copied to top]
MOV (R0),-(R3) ;Copy it onto top of stack.
CCC ;Clear condition code.
RTS PC ;Done
REPLAC: MOV @IPC(R4),R0 ;Pick up argument.
BMPIPC ;Bump IPC
ADD R0,R0 ;Double R0 to make it in bytes
ADD R3,R0 ;R0 ← LOC[stack element to be copied into]
MOV (R3)+,(R0) ;Copy verge of stack into it.
CCC ;Clear condition code.
RTS PC ;Done
FLUSH: MOV STKBAS(R4),R3;Reset the stack base.
CCC ;Clear condition code.
RTS PC ;Done
;Global reference routines GLBLNK, GLOBSR.
GLBLNK: ;Interpreter routine
COMMENT ⊗ Expects two arguments at the IPC, a level-offset, and two
words of a Rad50 name. Makes sure that this global is linked in to
the environment at the given level-offset. If not, a search is
made for it, and the result is put in the environment.
⊗
MOV @IPC(R4),R0 ;R0 ← level-offset
BMPIPC ;Bump IPC past the level-offset
JSR PC,GETARG ;R0 ← LOC[environment cell]
TST (R0) ;Graph node yet?
BEQ GLOBG1 ;No, must search for it
GLOBG2: BMPIPC ;Bump IPC past the Rad50 name
BMPIPC ;Bump IPC past the Rad50 name
RTS PC ;Done
GLOBG1: MOV R0,R2 ;R2 ← LOC[environment cell]
MOV IPC(R4),R0 ;R0 ← LOC[Rad50 representation]
JSR PC,GLOBSR ;R0 ← LOC[new or old graph node]
MOV R0,(R2) ;Stow LOC[graph node] in the environment cell
BR GLOBG2 ;Ready to return
MAXGLB == 10 ;Maximum number of globals allowed
GLBTAB: .BLKW 3*MAXGLB ;Three words per global: 2 of Rad50, one
;pointer to the graph node.
;To be searched linearly.
GLBLIM: .BLKW 3 ;Overflow place for GLBTAB
GLBEND: .BLKW 1 ;Points to next free place in GLBTAB
GLOBSR:
COMMENT ⊗ R0 = LOC[two words of Rad50]. Tries to find the
appropriate graph node using the GLBTAB, and if it fails, makes a new
graph node and inserts it in the GLBTAB. In any case, returns R0 ←
LOC[new or old graph node]. ⊗
EVWAIT GLBEVT ;Critical region starts here
MOV GLBEND,R1 ;R1 ← LOC[next free place in GLBTAB]
MOV (R0),(R1)+ ;Put the word sought at next free place
MOV 2(R0),(R1)+ ;
CLR (R1) ; with a 0 for a graph node pointer.
MOV #GLBTAB,R1 ;R1 ← LOC[start of GLBTAB]
GLOBS3: CMP (R0),(R1) ;MATCH?
BNE GLOBS1 ;No.
CMP 2(R0),2(R1) ;Second word match?
BEQ GLOBS2 ;Yes.
GLOBS1: ADD #6,R1 ;
BR GLOBS3 ;Try again.
GLOBS2: MOV 4(R1),R0 ;R0 ← LOC[graph node]
BNE GLOBS6 ;If it is not zero, we are done
ADD #6,GLBEND ;Move the end of the table down one entry
CMP GLBEND,#GLBLIM ;Too far?
BLT GLOBS5 ;No
HALERR GLOBMS ;Yes
GLOBS5: MOV R1,-(SP) ;Save place in GLBTAB
CLR R0 ;New graph node should have no value cell.
JSR PC,MAKEVN ;R0 ← LOC[a new variable node]
MOV (SP)+,R1 ;Restore place in GLBTAB
MOV R0,4(R1) ;store LOC[new graph node] in GLBTAB
GLOBS6: EVSIG GLBEVT ;Critical region ends here
RTS PC ;Done
GLOBMS: ASCIE </TOO MANY GLOBALS/>
;Flow-of-control: PROC, RETURN
PROC:
;Procedure call. Arguments:
; Destination.
; List of variables which are to be inserted in appropriate
; locations in the local storage of procedure. These are
; in the format variable (ie level-offset pair), new offset
; (right justified in the second word).
; There is a zero word to finish these.
;At the destination address can be found:
II == 0
XX FSLGTH ;Number of words to get from free storage
;for local variable pointers
XX PLEV ;Lexical level of procedure
DSLGTH == II ;Number of words before code starts
;Value parameters should have first been copied first into local temps
; (which have been arranged by the compiler), and then the temps are
; passed by reference. Eventual problem: to know which variables to
; really kill as the procedure is exited.
MOV @IPC(R4),R2 ;R2 ← LOC[destination]
BMPIPC ;Bump IPC
MOV FSLGTH(R2),R0 ;R0 ← Number of words to get.
JSR PC,GTFREE ;R0 ← LOC[block with that number of words]
;initialize pointer to lexical level:
MOV PLEV(R2),R1 ;R1 ← Lexical level of procedure
MOV ENV(R4),R2 ;R2 ← LOC[current environment]
SUB LEV(R4),R1 ;R1 ← Difference in levels: desired-got
BEQ PRC1 ;Diff=0; can use R2 as pointer at right environment.
PRC2: MOV SLINK(R2),R2;No, must go up a level. R2 ← LOC[base of upper area]
INC R1 ;R1 ← New difference in levels
BNE PRC2 ;If not yet good, then move up another level
PRC1: MOV R2,SLINK(R0);SLINK[new environment] ← correct global environment
;Put copies of local variables in new area
MOV R0,-(SP) ;Stack LOC[new environment]
MOV @IPC(R4),R0 ;R0 ← level-offset pair for an argument
BEQ PRC3 ;If there are no more, go to next phase
PRC4: BMPIPC ;Else bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[graph node]]
MOV @IPC(R4),R1 ;R1 ← offset in new block
BMPIPC ;Bump IPC
ADD (SP),R1 ;R1 ← LOC[place in new environment to put pointer]
MOV (R0),(R1) ;new environment gets pointer to LOC[argument graph node]
MOV @IPC(R4),R0 ;R0 ← level-offset pair for an argument
BNE PRC4 ;If there are more, go back and treat them
PRC3: BMPIPC ;Bump IPC one last time
;Save the old context in the new area
MOV (SP)+,R1 ;R1 ← LOC[new environment]
MOV LEV(R4),OLEV(R1) ;Store the old level
MOV ENV(R4),OENV(R1) ;Store the old environment location
MOV IPC(R4),OIPC(R1) ;Store the return address
;Set up the new context for procedure
MOV PLEV(R2),LEV(R4) ;New lexical level
MOV R1,ENV(R4) ;New environment location
ADD #DSLGTH,R2 ;R2 ← Place where execution should begin
MOV R2,IPC(R4) ;New program counter
CCC ;Clear condition code.
RTS PC ;Done
RETURN:
;Returns from a procedure call to calling program. Since variables are
;passed by reference, it is not necessary to do any copying of values.
;All that is needed is to restore the context of the caller and to
;discard the display.
MOV ENV(R4),R0 ;R0 ← LOC[current environment]
MOV OLEV(R0),LEV(R4) ;Restore the old lexical level
MOV OENV(R0),ENV(R4) ;Restore the old environment
MOV OIPC(R0),IPC(R4) ;Restore the IPC
JSR PC,RLFREE ;Release storage of old display
CCC ;Clear condition code.
RTS PC ;Done
; FORCHK, JUMP, JUMPC
FORCHK:
;Assume that the stack has, from surface in, the increment, the
; final value, and the control variable's value, all of which are
; scalar values. If (FINAL-CONVAR)*(INCREMENT) ≥ 0 then this is a
; no-op; otherwise, jump to the destination.
;Arguments: destination.
LDF @2(R3),AC0 ;AC0 ← final value
SUBF @4(R3),AC0 ;AC0 ← final - current
MULF @(R3),AC0 ;AC0 ← (final - current)*increment
MOV @IPC(R4),R0 ;R0 ← destination
BMPIPC ;Bump IPC
CFCC ;
BGE FOR1 ;Shall this be a no-op?
MOV R0,IPC(R4) ;No; set new IPC.
FOR1: CLR R0 ;
RTS PC ;Done
JUMP:
;Takes one argument: the new address.
MOV @IPC(R4),IPC(R4)
CCC ;Clear condition code.
RTS PC ;Done
JUMPC: ;Interpreter routine
COMMENT ⊗ Two arguments: the condition and the destination address.
The condition queries the top of the stack and pops it, assuming it
to be a scalar. The interpreter jumps to the destination address if
the condition is satisfied. The possible conditions are 0(Never),
1(L), 2(E), 3(LE), 4(Always), 5(GE), 6(NE), 7(G). Note that
comparisons of equality must be exact to floating precision. ⊗
MOV @IPC(R4),R2 ;R2 ← condition
BMPIPC ;Bump IPC
BLT JMPCERR ;If out of range, complain.
MOV R2,R0 ;
SUB #7,R0 ;
BGT JMPCERR ;
MOV (R3)+,R0 ;R0 ← LOC[arg]
LDF (R0),AC0 ;AC0 ← arg
ADD R2,R2 ;
ADD R2,R2 ;Multiply condition by 4.
CFCC ;
JMP JMPC3(R2) ;Go to the right test.
JMPC3: BR JMPC1 ;N always fail
BR JMPC4 ;
BGE JMPC1 ;L
BR JMPC4 ;
BNE JMPC1 ;E
BR JMPC4 ;
BGT JMPC1 ;LE
BR JMPC4 ;
TST R0 ;A never fail
BR JMPC4 ;
BLT JMPC1 ;GE
BR JMPC4 ;
BEQ JMPC1 ;NE
BR JMPC4 ;
BLE JMPC1 ;G
JMPC4: MOV @IPC(R4),IPC(R4) ;Succeed
BR JMPC2 ;
JMPC1: BMPIPC ;Fail. Bump IPC
JMPC2: CCC ;Clear condition code.
RTS PC ;Done
JMPCER: HALERR JMPCMS ;
JMPCMS: ASCIE </ILLEGAL JUMPC CODE/>
; SPAWN, SPROUT, TERMINATE
SPAWN: ;Utility routine
COMMENT ⊗ Takes two arguments: In R0, the IPC of the interpreter to
spawn, and in R1, the event (if any) to put in EVT of the new
interpreter. The inferior will have the same environment as the
superior. Creates an interpreter status block, stack, process
descriptor, and is ready for a SCHEDU when it returns the process
descriptor in R0. ⊗
MOV R1,-(SP) ;Save the EVT
MOV R0,-(SP) ;Save the new IPC
MOV #ISBS,R0 ;R0 ← Size (in words) of an interpreter status block
JSR PC,GTFREE ;R0 ← LOC[new interpreter status block]
MOV (SP)+,IPC(R0);new IPC ← first argument
MOV ENV(R4),ENV(R0) ;new ENV ← old ENV
MOV LEV(R4),LEV(R0) ;new LEV ← old LEV
.IFNZ ALAID
MOV DEBMOD(R4),DEBMOD(R0) ;new DEBMOD ← old DEBMOD
.ENDC
EVWAIT INTEVT ;Interlock sensitive operation.
MOV #NXTINT+ISTBLK,R1 ;Link into the interpreter list.
MOV (R1),NXTINT(R0) ;
MOV R0,(R1) ;
EVSIG INTEVT ;End of interlock
MOV (SP)+,EVT(R0);new EVT ← second argument.
MOV R0,-(SP) ;Save LOC[new interpreter status block]
MOV #INSTSZ,R0 ;R0 ← Size needed for an interpreter stack
JSR PC,GTFREE ;R0 ← LOC[new interpreter stack]
MOV (SP)+,R1 ;R1 ← LOC[new interpreter status block]
MOV R0,STKBAS(R1) ;Store away new stack base
ADD #2*INSTSZ,R0 ;R0 ← LOC[top of new stack] (INSTSZ is in bytes)
MOV R1,-(SP) ;Save R1
MOV R0,-(SP) ;Save R0
MOV #210,R0 ;Room for process descriptor
JSR PC,GTFREE ;R0 ← LOC[new process descriptor]
MOV #UFPUSE+UGRSAV,PDBSTA(R0);Use floating point, use saved registers.
MOV #420,UPDLEN(R0) ;Length of PCB
; MOV (R2),PDBR2(R0) ;Transfer register 2 (not currently necessary)
MOV (SP)+,R1 ;R1 ← LOC[new interpreter stack top]
MOV R1,PDBR3(R0) ;Store away new interp stack pointer (reg 3)
MOV (SP)+,R1 ;R1 ← LOC[new ISB]
MOV R0,PCB(R1) ;Store away LOC[PCB] in new ISB
MOV R1,PDBR4(R0) ;Store away LOC[ISB] in reg 4 of PCB
; MOV R5,PDBR5(R0) ;Store away reg 5 (not currently necessary)
MOV SP,R1 ;
TST (R1)+ ;
MOV R1,PDBSP(R0) ;Store away the new stack pointer (reg 6)
MOV #INTERP,PDBPC(R0);Store away the new PC
.IFNZ K2
MOV PCB(R4),R1 ;Use same UIMAP, UDMAP that we are using.
MOV UIMAP(R1),UIMAP(R0) ;
MOV UDMAP(R1),UDMAP(R0) ;
.ENDC
RTS PC ;Done
; These are the appropriate scheduling commands:
; SCHEDU R0,#INTERP,#0,#2;Cause the new process to be started, suspended
; FORK R0,#INTERP,#0 ;Cause the new process to be started.
SPROUT: ;Interpreter routine
COMMENT ⊗ Arguments: One address in pseudo-code for each of the
several forks starting up, followed by a 0 word. This is to be used
only for cobegins, not for servos. Each new interpreter is given an
interpreter status block and is then scheduled. As each terminates,
it signals its defining event. Since each of these has the same
event, the current interpreter need only wait until they all happen.
⊗
MOV R3,-(SP) ;Save R3. Caution: cannot use interpreter stack now.
CLR R3 ;R3 is the count of how many inferiors to spawn.
EVMAK ;-(SP) ← Event identifier for communication with infs.
SPR2: MOV @IPC(R4),R0 ;R0 ← next argument (IPC)
BEQ SPR1 ;If zero, then we have spawned all the inferiors.
BMPIPC ;Bump IPC
INC R3 ;Count it.
MOV (SP),R1 ;R1 ← event for the inferior EVT
JSR PC,SPAWN ;
MOV R0,R2 ;R2 ← new process control block
;Set up the new environment
JSR PC,NEWENV ;R0 ← LOC[new environment]
MOV ENV(R4),SLINK(R0) ;Not necessary to set up OLEV, etc.
MOV PDBR4(R2),R1;
MOV R0,ENV(R1) ;
INC LEV(R1) ;
SCHEDU R2,#INTERP,#0,#2;Cause the new process to be started, suspended
BR SPR2 ;Go handle the next inferior.
SPR1: BMPIPC ;Bump IPC
SPR4: DEC R3 ;Another wait to be done?
BMI SPR3 ;No, we are finished.
EVWAIT (SP) ;Wait for an inferior to come back.
BCC SPR4 ;If all well, wait for the next one.
HALERR SPRMES ;The event was killed!
SPR3: EVKIL (SP)+ ;Kill the event now, remove from stack
MOV (SP)+,R3 ;Restore R3
CCC ;Clear condition code.
RTS PC ;Done
SPRMES: ASCIE /BAD RETURN FROM INFERIOR/
TERMINATE:
COMMENT ⊗ Interpreter routine, sometimes jumped to from other
interpreter routines. End this interpreter. ⊗
MOV EVT(R4),R0 ;R0 ← event to announce imminent demise
BEQ TERM1 ;If there is one
EVSIG R0 ;Announce that we are about to disappear.
TERM1: MOV STKBAS(R4),R0 ;Reclaim interpreter stack
JSR PC,RLFREE ;
MOV PCB(R4),R0 ;Reclaim process control block (may be dangerous)
JSR PC,RLFREE ;
MOV R4,R0 ;Reclaim Interpreter Status Block
JSR PC,RLFREE ;
EVWAIT INTEVT ;Enter critical region.
MOV #ISTBLK,R0 ;The following unlinks this interpreter from the chain.
TERM3: MOV R0,R1 ;
MOV NXTINT(R1),R0;
CMP R0,R4 ;Have we found ours yet?
BNE TERM3 ;
MOV NXTINT(R4),NXTINT(R1); Yes. rechain.
EVSIG INTEVT ;Leave critical region.
DISMIS ;Go away
;Calculator routines: MEXP, MCLC, DCLC, ENDCLC;
COMMENT ⊗ Make an expression, put it in enviroment. Arguments are
the needed list (level-offset list, terminated by 0), the IPC
(ablsolute address), and the offset. ⊗
MEXP: ;Interpreter routine.
;form the needed list
CLR -(SP) ;Start with null needed list on the stack
MEXP1: MOV @IPC(R4),R0 ;R0 ← the next needed level-offset
BEQ MEXP2 ;Any more?
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[next needed graph node]]
MOV (R0),-(SP) ;Stack next needed graph node
JSR PC,NEWCEL ;R0 ← LOC[new cell]
MOV (SP)+,CAR(R0) ;LOC[Needed graph node]
MOV (SP),CDR(R0);Link to rest of needed list
MOV R0,(SP) ;New needed list
BR MEXP1 ;Repeat
MEXP2: BMPIPC ;Bump IPC past the 0 at end of list
MOV (SP)+,R0 ;R0 ← needed list
MOV @IPC(R4),R1 ;R1 ← IPC
BMPIPC ;Bump IPC
CALL MAKEXP,<R4,R1,R0> ;R0 ← LOC[new expression node]
MOV @IPC(R4),R1 ;R1 ← offset
BMPIPC ;Bump IPC
BIC #177400,R1 ;Remove level info.
ADD ENV(R4),R1 ;R0 ← Pointer into environment
MOV R0,(R1) ;Stow away pointer to expression node
CCC ;Clear condition code
RTS PC ;Done
MCLC: ;Interpreter routine.
COMMENT ⊗ Takes two arguments: the level-offset of the expression,
and the level-offset of the variable for which this expression is to
be a calculator. ⊗
MOV @IPC(R4),R0 ;R0 ← Level-offset of expression
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[exression node]]
MOV (R0),R2 ;R2 ← LOC[expression node]
MOV @IPC(R4),R0 ;R0 ← level-offset of variable
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[variable node]]
CALL ADDCLC,<(R0),R2> ;Do the linking
CCC ;Clear condition code
RTS PC ;Done
DCLC: ;Interpreter routine
COMMENT ⊗ Takes two arguments: the level-offset of the expression,
and the level-offset of the variable from which this expression is to
be removed as a calculator. ⊗
MOV @IPC(R4),R0 ;R0 ← Level-offset of expression
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[exression node]]
MOV (R0),R2 ;R2 ← LOC[expression node]
MOV @IPC(R4),R0 ;R0 ← level-offset of variable
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[variable node]]
CALL REMCLC,<(R0),R2> ;Do the unlinking
CCC ;Clear condition code
RTS PC ;Done
ENDCLC: ;Interpreter routine.
COMMENT ⊗ Called as last instruction in a calculator cell. Returns
via an RTS RF with the value from the top of the stack in R0. Does
not unlink anything. ⊗
MOV RF,SP ;Reset the stack
TST -(SP) ;
MOV (R3)+,R0 ;Get the coveted value cell
RTS RF ;Will return to the calling point in EVLCLC.
;Changer routines: MCHGR, GTOLD, GTNEW
COMMENT ⊗ Make a changer for a graph node. This involves several
data: the target variable, specified as a level-offset pair, and the
location of the changer code, (which is ordinary interpreter code
which terminates with TERMINATE). These data are passed as arguments
to MCHG: target (level-offset), IPC (absolute address). Recall that
a changer cell looks like this:
II==0
XX NXTCHG ;next changer cell in chain
XX CHGISB ;Points to interpreter status block to resolve addressing
XX CHGIPC ;the interpeter PC where the calculation starts
CHGCSZ == II/2 ;Size of changer cell, in words
⊗
MCHG: ;Interpreter routine.
MOV R2,-(SP) ;Save R2
MOV R3,-(SP) ;Save R3
MOV #CHGCSZ,R0 ;Get room for a changer cell
JSR PC,GTFREE ;Note that we use large block allocation
MOV R0,R3 ;R3 ← LOC[new changer cell]
MOV R4,CHGISB(R3) ;store away ISB
MOV @IPC(R4),R0 ;R0 ← level-offset pair.
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[target graph node]]
MOV (R0),R2 ;R2 ← LOC[target graph node]
MOV @IPC(R4),CHGIPC(R3) ;store away target IPC
BMPIPC ;Bump IPC
CALL ADDCHG,<R2,R3> ;Do the final linking
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R2 ;Restore R2
CCC ;Clear condition code
RTS PC ;Done
GTOLD: ;Interpreter routine
COMMENT ⊗ Gets the OLD value that this changer (acting as an
interpreter) has access to; puts it on the stack. ⊗
MOV OLDV(R4),-(R3)
CCC ;Clear condition code
RTS PC ;Done
GTNEW: ;Interpreter routine
COMMENT ⊗ Gets the NEW value that this changer (acting as an
interpreter) has access to; puts it on the stack. ⊗
MOV NEWV(R4),-(R3)
CCC ;Clear condition code
RTS PC ;Done
;return scalars: SADD, SSUB, SMUL, SDIV, SNEG, VDOT, PVDOT, VMAG, SSBRTN
COMMENT ⊗ All timings are averages of 1000 runs. They take into
account the cost of the RTS but not the JSR. It is assumed that
GETSCA and GETVEC take no time. All routines on this page are
interpreter routines. ⊗
;30 microseconds
SADD: ;Scalar ← Scalar + Scalar
LDF @(R3)+,AC0 ;AC0 ← arg 2
ADDF @(R3)+,AC0 ;AC0 ← arg2 + arg1
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
CCC ;Clear condition code.
RTS PC ;Done
SSUB: ;Scalar ← Scalar - Scalar
LDF @2(R3),AC0 ;AC0 ← arg 1
SUBF @(R3)+,AC0 ;AC0 ← arg1 - arg2
TST (R3)+ ;Move past first argument
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
CCC ;Clear condition code.
RTS PC ;Done
;30 microseconds
SMUL: ;Scalar ← scalar * scalar
LDF @(R3)+,AC0 ;AC0 ← arg 2
MULF @(R3)+,AC0 ;AC0 ← arg2 * arg1
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
CCC ;Clear condition code.
RTS PC ;Done
;33 microseconds
SDIV: ;Scalar ← Scalar / Scalar
LDF @(R3)+,AC1 ;AC1 ← arg 2
LDF @(R3)+,AC0 ;AC0 ← arg 1
DIVF AC1,AC0 ;AC0 ← arg1 / arg2
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
CCC ;Clear condition code.
RTS PC ;Done
;26 microseconds
SNEG: ;Scalar ← -Scalar
LDF @(R3)+,AC0 ;AC0 ← arg
NEGF AC0 ;AC0 ← -arg
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
CCC ;Clear condition code.
RTS PC ;Done
;96 -- 116 microseconds
VDOT: ;Scalar ← Vector dot Vector
;S ← (X1X2 + Y1Y2 + Z1Z2) / W1W2
MOV R2,-(SP) ;Save R2.
MOV (R3)+,R1 ;R1 ← LOC[arg 2]
MOV (R3)+,R0 ;R0 ← LOC[arg 1]
CLRF AC0 ;AC0 ← 0. Running total
MOV #3,R2 ;R2 ← 3: Length of vector
VDV1: LDF (R0)+,AC1 ;Form sum of products of first 3 terms
MULF (R1)+,AC1 ;
ADDF AC1,AC0 ;
SOB R2,VDV1 ;Loop until all 3 fields done.
DIVF (R0),AC0 ;Divide by W1
DIVF (R1),AC0 ;Divide by W2. AC0 now has answer.
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
MOV (SP)+,R2 ;Restore R2
CCC ;Clear condition code.
RTS PC ;Done
;103 -- 116 microseconds
PVDOT: ;Scalar ← Plane dot Vector
;S ← X1X2 + Y1Y2 + Z1Z2 + W1W2
MOV R2,-(SP) ;Save R2.
MOV (R3)+,R1 ;R1 ← LOC[arg 2]
MOV (R3)+,R0 ;R0 ← LOC[arg 1]
CLRF AC0 ;AC0 ← 0. Running total
MOV #4,R2 ;R2 ← 4: Length of vector and weight
PDV1: LDF (R0)+,AC1 ;Form sum of products of all 4 terms
MULF (R1)+,AC1 ;
ADDF AC1,AC0 ;
SOB R2,PDV1 ;Loop until all 3 fields done.
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store result
MOV (SP)+,R2 ;Restore R2
CCC ;Clear condition code.
RTS PC ;Done
;199 -- 207 microseconds
VMAGN: ;Scalar ← Norm (vector)
;S ← SQRT(XX + YY+ ZZ) / W
MOV (R3)+,R1 ;R1 ← LOC[arg]
LDF (R1)+,AC0 ;AC0 ← X
MULF AC0,AC0 ;AC0 ← XX
LDF (R1)+,AC1 ;AC1 ← Y
MULF AC1,AC1 ;AC1 ← YY
ADDF AC1,AC0 ;AC0 ← XX + YY
LDF (R1)+,AC1 ;AC1 ← Z
MULF AC1,AC1 ;AC1 ← ZZ
ADDF AC1,AC0 ;AC0 ← XX + YY + ZZ
MOV R1,-(SP) ;Push LOC[W] onto system stack, to save across SQRTF
JSR PC,@LSQRTF ;AC0 ← SQRT(XX + YY + ZZ)
DIVF @(SP)+,AC0 ;AC0 ← AC0 / W
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store answer
CCC ;Clear condition code.
RTS PC ;Done
SSBRTN: ;Call a routine.
MOV (R3)+,R1 ;R1 ← LOC[arg]
LDF (R1),AC0 ;AC0 ← arg
MOV @IPC(R4),R0 ;R0 ← which routine (a small number)
BMPIPC ;Bump IPC
ASL R0 ;Double (words → bytes)
BLE SSBRT1 ;Too small.
CMP R0,#SBLSIZ ;Too large?
BGE SSBRT1 ;Yes
JSR PC,@SBRLST(R0) ;Call a routine. AC0 ← answer.
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store answer
CCC ;Clear condition code.
RTS PC ;Done
SSBRT1: HALERR SSBRMS ;Complain
SCC ;Set condition code
RTS PC ;Done
SSBRMS: ASCIE </NO SUCH SUBROUTINE/>
SBRLST: ;List of legal subroutines
0 ;Illegal
SQRT ;The only one right now. #1
SBLSIZ == .-SBRLST ;The size of the list (bytes)
SQRT: JMP @LSQRTF ;Let it do the returning
;Vector utilities: UNITV, CROSV
COMMENT ⊗ These are not currently being used
;281 -- 286 microseconds
UNITV: ;Vector ← V / Norm(V)
;S ← SQRT(XX + YY+ ZZ) / W
MOV (R3),R1 ;R1 ← LOC[arg]
LDF (R1)+,AC0 ;AC0 ← X
MULF AC0,AC0 ;AC0 ← XX
LDF (R1)+,AC1 ;AC1 ← Y
MULF AC1,AC1 ;AC1 ← YY
ADDF AC1,AC0 ;AC0 ← XX + YY
LDF (R1)+,AC1 ;AC1 ← Z
MULF AC1,AC1 ;AC1 ← ZZ
ADDF AC1,AC0 ;AC0 ← XX + YY + ZZ
MOV R1,-(SP) ;Save R1 across SQRTF
JSR PC,SQRTF ;AC0 ← SQRT(XX + YY + ZZ)
MOV (SP)+,R1 ;Restore R1
DIVF (R1),AC0 ;AC0 ← Norm = SQRT / W
MOV (R3)+,R1 ;R1 ← LOC[arg]
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector block]
MOV #3,R2 ;R2 ← count of fields
UNITV1: LDF (R1)+,AC1 ;AC1 ← field of vector
DIVF AC0,AC1 ;divide by norm
STF AC1,(R0)+ ;Store result
SOB R2,UNITV1 ;Loop until done
MOV (R1)+,(R0)+ ;Copy W.
MOV (R1),(R0) ; (two words long)
CCC ;Clear condition code
RTS PC ;Done
;172 -- 184 microseconds
CROSV: ;Vector ← Vector cross Vector
;X ← Y1Z2 - Y2Z1
;Y ← X2Z1 - X1Z2
;Z ← X1Y2 - X2Y1
;W ← W1W2
;AC0, 1, 2, 3, 4, 5 are garbaged by this routine.
MOV (R3),R2 ;R2 ← LOC[arg 2]
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector block]
MOV 4(R3),R1 ;R1 ← LOC[arg 1]. Must not pop R3 stack yet!
LDF 14(R1),AC0 ;AC0 ← W1
MULF 14(R2),AC0 ;AC0 ← W1W2
STF AC0,14(R0) ;Store AC0 → W
LDF 4(R1),AC0 ;AC0 ← Y1
LDF (R2),AC1 ;AC1 ← X2
LDF 4(R2),AC2 ;AC2 ← Y2
LDF (R1),AC3 ;AC3 ← X1
STF AC3,AC4 ;AC4 ← X1
STF AC0,AC5 ;AC5 ← Y1
MULF AC2,AC3 ;AC3 ← X1Y2
MULF AC1,AC0 ;AC0 ← X2Y1
SUBF AC0,AC3 ;AC3 ← X1Y2 - X2Y1
STF AC3,10(R0) ;Z ← AC3
LDF 10(R2),AC0 ;AC0 ← Z2
LDF 10(R1),AC3 ;AC3 ← Z1
MULF AC4,AC0 ;AC0 ← X1Z2
MULF AC3,AC1 ;AC1 ← X2Z1
SUBF AC0,AC1 ;AC1 ← X2Z1 - X1Z2
STF AC1,4(R0) ;Y ← AC1
LDF 10(R2),AC0 ;AC0 ← Z2
MULF AC5,AC0 ;AC0 ← Y1Z2
MULF AC2,AC3 ;AC3 ← Y2Z1
SUBF AC3,AC0 ;AC0 ← Y1Z2 - Y2Z1
STF AC0,(R0) ;X ← AC0
MOV (R3)+,2(R3) ;Put result cell where first argument was
TST (R3)+ ;Put stack pointer in right place
CCC ;Clear condition code
RTS PC ;Done
⊗ END OF COMMENTED-OUT PROCEDURES.
;Return vectors: SVMUL, TVMUL, VMAKE, VADD
;83 -- 91 microseconds
SVMUL: ;Vector ← Scalar * Vector. Interpreter routine
;X ← S*X, Y ← S*Y, Z ← S*Z, W ← W
MOV (R3)+,R2 ;R2 ← LOC[vector]
LDF @(R3)+,AC0 ;AC0 ← scalar;
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector block]
MOV #3,R1 ;R1 ← 3: How many fields to handle
SVM1: LDF (R2)+,AC1 ;AC1 ← next field of vector
MULF AC0,AC1 ;AC1 ← product
STF AC1,(R0)+ ;Store result
SOB R1,SVM1 ;Loop until all 3 fields done.
MOV (R2)+,(R0)+ ;Transfer W
MOV (R2)+,(R0)+ ; which is 2 words long.
CCC ;Clear condition code
RTS PC ;Done
VMAKE: ;Interpreter routine
LDF @(R3)+,AC1 ;Fetch X
LDF @(R3)+,AC2 ;Fetch Y
LDF @(R3)+,AC3 ;Fetch Z
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector]
STF AC1,(R0)+ ;Store X
STF AC2,(R0)+ ;Store Y
STF AC3,(R0)+ ;Store Z
MOV ONE,(R0)+ ;Store W
CLR (R0) ;Store W (second word)
CCC ;Clear condition code
RTS PC ;Done
VADD: ;Interpreter routine
MOV (R3)+,R0 ;R0 ← LOC[arg 1]
MOV (R3)+,R1 ;R1 ← LOC[arg 1]
LDF (R0)+,AC1 ;Calculate X
ADDF (R1)+,AC1 ;
LDF (R0)+,AC2 ;Calculate Y
ADDF (R1)+,AC2 ;
LDF (R0)+,AC3 ;Calculate Z
ADDF (R1)+,AC3 ;
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector]
STF AC1,(R0)+ ;Store X
STF AC2,(R0)+ ;Store Y
STF AC3,(R0)+ ;Store Z
MOV ONE,(R0)+ ;Assume W is 1
CLR (R0) ;
CCC ;Clear condition code
RTS PC ;Done
;283 -- 324 microseconds
TVMUL: ;Vector ← Trans * Vector. Interpreter routine
MOV (R3),R2 ;R2 ← LOC[vector]
MOV 2(R3),R0 ;R0 ← LOC[trans]
CLRF AC1 ;X ← 0
CLRF AC2 ;Y ← 0
CLRF AC3 ;Z ← 0
MOV #4,R1 ;R1 ← How many columns left to go
TVM1: LDF (R2)+,AC0 ;AC0 ← field of vector
STF AC0,AC5 ;AC5 ← copy of AC0
MULF (R0)+,AC0 ;
ADDF AC0,AC1 ;Add partial result to X
LDF AC5,AC0 ;Restore AC0
MULF (R0)+,AC0 ;
ADDF AC0,AC2 ;Add partial result to Y
LDF AC5,AC0 ;Restore AC0
MULF (R0)+,AC0 ;
ADDF AC0,AC3 ;Add partial result to Z.
ADD #4,R0 ;Skip bottom row
SOB R1,TVM1 ;Go back to do all 4 columns.
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[new vector]
STF AC1,(R0)+ ;Store X
STF AC2,(R0)+ ;Store Y
STF AC3,(R0)+ ;Store Z
MOV -4(R2),(R0)+;Copy W from the vector
MOV -2(R2),(R0) ; (2 words long)
MOV (R3)+,2(R3) ;Put result cell where first argument was
TST (R3)+ ;Put stack pointer in right place
CCC ;Clear condition code
RTS PC ;Done
;Return a trans: TMAKE, TVADD, TTMUL, TINVRT
TMAKE: ;Interpreter routine.
;All that is required is to take the rot part of the first argument,
;and the vector from the second part;
MOV (R3)+,R1 ;R1 ← LOC[arg 1]
MOV (R3)+,-(SP) ;Push LOC[arg 2]
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[new trans]
MOV #14,R2 ;R2 ← Count of how many copies to make
TMK1: MOV (R1)+,(R0)+ ;Transfer first half of floating word
MOV (R1)+,(R0)+ ;Transfer second half of floating word
SOB R2,TMK1 ;Repeat until done
MOV (SP)+,R1 ;R1 ← LOC[arg 2]
MOV #4,R2 ;R2 ← Count of how many copies to make
TMK2: MOV (R1)+,(R0)+ ;Transfer first half of floating word
MOV (R1)+,(R0)+ ;Transfer second half of floating word
SOB R2,TMK2 ;Repeat until done
CCC ;Clear condition code.
RTS PC ;Done.
TVADD: ;Interpreter routine.
;All that is required is to take the rot part of the first argument,
;and add the vector from the first part to the second argument.
MOV (R3)+,R1 ;R1 ← LOC[arg 1]
MOV (R3)+,R2 ;R2 ← LOC[arg 2]
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[new trans]
MOV R3,-(SP) ;Save R3
MOV #14,R3 ;R3 ← Count of how many copies to make
TVA1: MOV (R1)+,(R0)+ ;Transfer first half of floating word
MOV (R1)+,(R0)+ ;Transfer second half of floating word
SOB R3,TVA1 ;Repeat until done
MOV #3,R3 ;R3 ← Count of how many additions to perform
TVA2: LDF (R1)+,AC0 ;AC0 ← word from trans
ADDF (R2),AC0 ; + word from vector
STF AC0,(R0)+ ;
SOB R3,TVA2 ;Repeat until done
MOV ONE,(R0)+ ;Set last word to 1.0
CLR (R0) ;
MOV (SP)+,R3 ;Restore R3
CCC ;Clear condition code.
RTS PC ;Done.
TTMUL: ;Interpreter routine
;Multiplies two transes together. Takes advantage of the fact that
;last row is 0 0 0 1.
MOV R4,-(SP) ;Save R4
MOV (R3)+,R2 ;R2 ← LOC[arg 2]
MOV (R3)+,R4 ;R4 ← LOC[arg 1]
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[new trans]
MOV R3,-(SP) ;Save R3
MOV #4,R1 ;Loop count for cols of answer
MOV R4,-(SP) ;Save a copy of R4
TTM2: LDF (R2)+,AC1 ;Pick up a column of arg2: First row
LDF (R2)+,AC2 ; Second row
LDF (R2)+,AC3 ; Third row
STF AC3,AC4 ; store in AC4
ADD #4,R2 ; Fourth row is zero
MOV #3,R3 ;Loop count for rows of answer
TTM1: LDF (R4),AC3 ;First col of arg 1
MULF AC1,AC3 ;
LDF 20(R4),AC0 ;Second col of arg 1
MULF AC2,AC0 ;
ADDF AC0,AC3 ;
LDF 40(R4),AC0 ;Third col of arg 1
MULF AC4,AC0 ;
ADDF AC0,AC3 ;
STF AC3,(R0)+ ;
ADD #4,R4 ;Move to next column of arg 1
SOB R3,TTM1 ;Repeat for first 3 rows of answer
CLR (R0)+ ;Last row of answer is zero
CLR (R0)+ ;
MOV (SP),R4 ;Reset R4 to point to first row of arg 1
SOB R1,TTM2 ;Repeat for all four columns of answer
LDF -20(R0),AC1 ;Add correction for last column, first row
ADDF 60(R4),AC1 ;
STF AC1,-20(R0) ;
LDF -14(R0),AC1 ;Add correction for last column, second row
ADDF 64(R4),AC1 ;
STF AC1,-14(R0) ;
LDF -10(R0),AC1 ;Add correction for last column, third row
ADDF 70(R4),AC1 ;
STF AC1,-10(R0) ;
MOV ONE,-4(R0) ;Make last col, last row get a one.
TST (SP)+ ;Pop the R4 temp
MOV (SP)+,R3 ;Restore R3
MOV (SP)+,R4 ;Restore R4
CCC ;Clear condition code
RTS PC ;Done
TINVRT: ;Interpreter routine
COMMENT ⊗ Inverts a trans. Takes advantage of fact that last row is
0 0 0 1. The result, (rot',trslat'), is defined:
rot' = transpose(rot)
trslat' = -(rot'*trslat)
⊗
MOV (R3)+,R2 ;R2 ← LOC[old trans], travels down the whole trans
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[new trans] + 4*interation number
MOV R3,-(SP) ;Save R3
MOV R4,-(SP) ;Save R4
MOV R0,R3 ;R3 ← LOC[new trans] + 20*interation number
MOV R2,R4 ;R4 ← LOC[old trans], stays constant
MOV #3,R1 ;Three columns to do
TINV1: ;Transpose a column, multiplying by the translation
CLRF AC1 ;Cumulative product
LDF (R2)+,AC0 ;Take from the source rotation
STF AC0,(R0) ; into the transpose,
MULF 60(R4),AC0 ;
SUBF AC0,AC1 ;accumulate the product.
LDF (R2)+,AC0 ;Take from the source rotation
STF AC0,20(R0) ; into the transpose,
MULF 64(R4),AC0 ;
SUBF AC0,AC1 ;accumulate the product.
LDF (R2)+,AC0 ;Take from the source rotation
STF AC0,40(R0) ; into the transpose
MULF 70(R4),AC0 ;
SUBF AC0,AC1 ;accumulate the product
MOV (R2)+,14(R3); the bottom row of zeroes
MOV (R2)+,16(R3); the bottom row of zeroes
STF AC1,60(R0) ;Place the new translation
ADD #4,R0 ;Move to next row of result
ADD #20,R3 ;Move to next column of result
SOB R1,TINV1 ;
MOV ONE,14(R3) ;The one in last row, last column
CLR 16(R3) ; "
MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
CCC ;Clear condition code
RTS PC ;Done
;Motion: MOVE, CENTER, STOP, WHERE
.IFNZ MOVING ;If this version is supposed to be able to move
MOVE: ;Interpreter routine
MOV LMOVE,R2 ;Set for moving operation
JMP MOVSTA ;Use the common move code
CENTER: ;Interpreter routine
MOV LCENTER,R2 ;Set for centering operation
JMP MOVSTA ;Use the common move code
DVBKSZ == 34 ;Size of a device block
COMMENT ⊗ New version to update the frame afterwords. Assumes that
there are two arguments: a pointer to the trajectory table and a word
of mechanism bits, to help in updating the necessary variables. ⊗
MOVSTA: MOV #'π,R0 ;Whistle while you work
JSR PC,TYPCHR ;
MOV #DVBKSZ,R0 ;Get a device block
JSR PC,GTFREE ;
MOV R0,R1 ;R1 ← address of device block
MOV R0,-(SP) ;Save a copy on the stack
MOV @IPC(R4),R0 ;R0 ← address of coefficient list
JSR PC,@R2 ;Do some kind of move (MOVE, CENTER)
TST R0 ;Did the move succeed?
BEQ MOV6 ;Yes
HALERR MOVERR ;No, better complain.
MOV6: BMPIPC ;Bump IPC
MOV @IPC(R4),R2 ;R2 ← mechanism bits
BMPIPC ;Bump IPC
;Invalidate the affected device variables;
BIT #YARM,R2 ;
BEQ MOV2 ;
MOV #YAOFST,R0 ;
JSR PC,GETARG ;
CALL INVLDT,<(R0)>
MOV2: BIT #YHAND,R2 ;
BEQ MOV3 ;
MOV #YHOFST,R0 ;
JSR PC,GETARG ;
CALL INVLDT,<(R0)>
MOV3: BIT #BARM,R2 ;
BEQ MOV4 ;
MOV #BAOFST,R0 ;
JSR PC,GETARG ;
CALL INVLDT,<(R0)>
MOV4: BIT #BHAND,R2 ;
BEQ MOV1 ;
MOV #BHOFST,R0 ;
JSR PC,GETARG ;
CALL INVLDT,<(R0)>
MOV1: MOV (SP)+,R0 ;
JSR PC,RLFREE ;Get rid of the device block
CCC ;Clear condition code
RTS PC ;Return
RETRY: TST (SP)+ ;Get here from HALERR; clean off stack
MOV (SP)+,R0 ;
JSR PC,RLFREE ;Get rid of the device block
BACKIPC ;Backup IPC
RTS PC ;
MOVERR: ASCIE </SERVO ERROR. ERROR BITS IN R0. DEVICE BLOCK AT (R1).
TO RETRY THE MOVE, RETRY$G/>
.IFF ;If not a moving version
MOVE:
CENTER:
HALERR MOVERR ;Can't move
BMPIPC ;Bump IPC
BMPIPC ;Bump IPC
CLR R0 ;
RTS PC ;Return
MOVERR: ASCIE </SORRY, THIS VERSION CAN'T EVEN LIFT A FINGER/>
.ENDC
STOP: ;Interpreter routine
COMMENT ⊗ Takes one argument, a set of mechanism bits. For each
one on, all the associated joints are stopped. ⊗
MOV @IPC(R4),R2 ;R2 ← mechanism bits
BMPIPC ;Bump IPC
MOV R2,R0 ;R0 ← mech bits
JSR PC,TABOFS ;R0 ← table offset
BIT #AHAND,R2 ;A hand?
BNE STOP1 ;Yes
MOV #6,R1 ;R1 ← count of joints
BR STOP2 ;
STOP1: MOV #1,R1 ;R1 ← count of joints
STOP2: MOV @LDVCPTR(R0),R2 ;R2 ← device block pointer for this servo
BEQ STOP3 ;If any
BIS #100000,(R2);Stop this device.
STOP3: SOB R1,STOP2 ;Repeat
CCC ;Clear condition code
RTS PC ;Done
WHERE: ;Interpreter routine
COMMENT ⊗ One argument: The mechanism bits. Puts value of that
mechanism on the stack. Only one mechanism at a time, please! ⊗
MOV @IPC(R4),R2 ;Mechanism bits
BMPIPC ;Bump IPC
BIT #AHAND,R2 ;A hand?
BNE MCHV1 ;No.
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[new trans]
BR MCHV2 ;
MCHV1: JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar]
MCHV2: MOV LTHPTR,R1 ;
JSR PC,@LUPDATE ;
CCC ;Clear condition code
RTS PC ;Done
;Condition monitors: CMMAK
.IFNZ ONMONS
COMMENT ⊗ This is the second version of condition monitors (here
refered to as c-m's). Hardware-type c-m's are still not ready. The
checker and the body are the same job in this version; before 10/2/75
they were seperate. The basic operations are Creation, Enabling,
Disabling, Destruction. Creation causes a c-m control block to be
set up, and pointed to by the c-m variable. This block has the
following fields: ⊗
II == 0
XX CMSEVT ;The event used to awaken the tester upon enabling
XX CMTEVT ;The event for which this c-m tests, if any
XX CMFORC ;The FMCB needed, if any, for calculating forces
XX CMSTAT ;Status bits for the c-m
CMENB == 1 ;set => enabled
CMDES == 2 ;set => to be destroyed
CMCBSZ == II/2 ;Length in words of a c-m control block.
COMMENT ⊗ The once-only code of the c-m is sprouted at priority 3 (it
is an interpreter), and after initialization, it waits for the
gronking event CMSEVT. Enabling signals the event CMSEVT and sets
the enabled bit in CMSTAT. Disabling resets the enabled bit, and the
c-m will wait on the CMSEVT for future action. As long as the c-m is
enabled, it periodically wakes up, checks its status bits. If the
enable bit is reset, the c-m waits for CMSEVT. Else it checks the
condition. If it is satisfied, the c-m disables itself and
proceeds to the conclusion and level 1. (The conclusion should reset
itself to level 0 after all critical activity has been accomplished.)
Otherwise, it reschedules itself. If the destroy bit should ever be
set in CMSTAT, then the c-m will destroy the event CMSEVT. Then
it will reclaim the c-m control block and will dismiss, never to
return. (The pointer to the c-m in the environment should be zeroed
by the destroying angel.). ⊗
CMMAK: ;Interpreter routine
COMMENT ⊗ Takes three arguments, the offset of the nascent c-m, the
level-offset of the event that this monitor is to wait on, if any,
and the IPC of the c-m code. ⊗
MOV @IPC(R4),R2 ;R2 ← offset
BMPIPC ;Bump IPC
ADD ENV(R4),R2 ;R2 ← Pointer into environment
TST (R2) ;Already something there?
BEQ CMMK1 ;
HALERR CMMMSG ;Yes. complain.
;Make a c-m control block
CMMK1: MOV #CMCBSZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[c-m control block]
MOV R0,(R2) ;Stuff into environment
EVMAK ;
MOV (SP)+,CMSEVT(R0) ;Make an event for CMSEVT
CLR CMSTAT(R0) ;Disabled, undestroyed
CLR CMTEVT(R0) ;Not necessarily ON <event> DO
MOV R0,-(SP) ;Save LOC[c-m control block]
MOV @IPC(R4),R0 ;R0 ← level-offset of event this c-m waits for.
BMPIPC ;Bump IPC
TST R0 ;If any
BEQ CMMK2 ;
JSR PC,GETARG ;R0 ← LOC[environment location of event]
MOV (SP),R1 ;R1 ← LOC[c-m control block]
MOV (R0),CMTEVT(R1) ;Put the CMTEVT in the c-m control block.
;Prepare the c-m job
CMMK2: MOV @IPC(R4),R0 ;R0 ← IPC of c-m code
BMPIPC ;Bump IPC
CLR R1 ;C-m's do not expire with events
JSR PC,SPAWN ;R0 ← process control block for c-m
MOV PDBR4-PDBSTA(R0),R2;R2 ← PR4 = LOC[c-m's interpeter status block]
MOV (SP)+,CMCB(R2);Stuff CMCB of the c-m
FORK R0,#INTERP,#3;Cause the c-m to be started. It will go into wait.
CCC ;Clear condition code
RTS PC ;Done
CMMMSG: ASCIE </CMMAK: WILL CREATE EXISTENT CONDITION MONITOR/>
; CMENBL, CMDSBL, CMDEST, CMTRIG, CMSKED, CMUNCR
CMNEMS: ASCIE </TRYING TO TREAT NON-EXISTENT EVENT/>
CMENBL: ;Interpeter routine
; One argument, a level-offset pair for the c-m to enable.
MOV @IPC(R4),R0 ;R0 ← level-offset
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← pointer into environment
MOV (R0),R0 ;R0 ← pointer to c-m control block.
BEQ CMDERR ;If none, then error
BIS #CMENB,CMSTAT(R0) ;Set the enable bit
EVSIG CMSEVT(R0) ;Gronk the c-m
CCC ;Clear condition code
RTS PC ;Done
CMEERR: HALERR CMNEMS ;
SCC ;Set condition code
RTS PC ;
CMDSBL: ;Interpreter routine
; One argument, a level-offset pair for the c-m to disable.
MOV @IPC(R4),R0 ;R0 ← level-offset
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← pointer into environment
MOV (R0),R0 ;R0 ← pointer to c-m control block.
BEQ CMDERR ;If none, then error
BIC #CMENB,CMSTAT(R0) ;Clear the enable bit
CCC ;Clear condition code
RTS PC ;Done
CMDERR: HALERR CMNEMS ;
SCC ;Set condition code
RTS PC ;
CMDEST: ;Interpreter routine
COMMENT ⊗ Argument list. Each is an offset for the c-m to destroy.
The list is terminated with a zero entry. ⊗
MOV @IPC(R4),R0 ;R0 ← offset
BEQ CMDS1 ;If 0, then done
BMPIPC ;Bump IPC
ADD ENV(R4),R0 ;R0 ← pointer into environment
MOV (R0),R1 ;R1 ← LOC[c-m control block]
BEQ CMDSER ;If none, then error
BIS #CMDES,CMSTAT(R1) ;Set the destroy bit
EVKIL CMSEVT(R1);Destroy the event. That ought to wake him up!
CLR (R0) ;Remove c-m from environment
BR CMDEST ;Go do the next one.
CMDS1: BMPIPC ;Bump IPC the last time
CCC ;Clear condition code
RTS PC ;Done
CMDSER: HALERR CMNEMS ;
SCC ;Set condition code
RTS PC ;
CMTRIG: ;Interpeter routine
COMMENT ⊗ Should be executed only from a c-m. Sets the priority to 1
and disables the checker. ⊗
MOV CMCB(R4),R0 ;
CMTR1: EVTST CMSEVT(R0);Eat all signals enabling the checker.
BCC CMTR1 ;
BIC #CMENB,CMSTAT(R0) ;Clear the enable bit
MOV PCB(R4),R0 ;
CLR 2(R0) ;Clear word 1 of process control block to reset nominal
; priority to 0.
SETPRI #1 ;Set the priority to 1
TST (SP)+ ;Discard old priority
CCC ;Clear condition code
RTS PC ;Done
CMSKED: ;Interpreter routine
COMMENT ⊗ Goes to sleep a while (currently, 100 milliseconds). Upon
awakening, checks the status bits of this checker, and either
dismisses, waits, or returns. ⊗
SETPRI #3 ;In case the conclusion left it at 1 or 0.
TST (SP)+ ;Flush old priority
MOV @IPC(R4),-(SP) ;Waiting interval
BMPIPC ;Bump IPC
SLEEP ;Sleep a while
MOV CMCB(R4),R0 ;R0 ← c-m control block
CMSK4: BIT #CMDES,CMSTAT(R0) ;Destroy bit set?
BEQ CMSK1 ;No
EVKIL CMSEVT(R0);Yes. Kill the triggering event.
CMSK3: JSR PC,RLFREE ;Return the c-m control block
JMP TERMINATE ;Use the interpeter terminate routine.
CMSK1: BIT #CMENB,CMSTAT(R0) ;Enable bit set?
BNE CMSK2 ;Yes.
EVWAIT CMSEVT(R0);No. Wait until signaled by the enabler
BCS CMSK3 ;If the enabling event died, so must we.
BR CMSK4 ;Else start from the awakening point.
CMSK2: MOV CMTEVT(R0),R1 ;R1 ← event to test for
BEQ CMSK5 ;If any
EVWAIT R1 ;Wait for event to happen
BIT #CMENB,CMSTAT(R0) ;Still enabled?
BNE CMSK5 ;Yes. May exit.
EVSIG R1 ;Oops, we were disabled! Resignal the event.
BR CMSK4 ;And try again.
CMSK5: CCC ;Clear condition code
RTS PC ;Done
CMUNCR: ;Interpreter routine.
COMMENT ⊗ Used in body of c-m. Starts uncritical section. ⊗
MOV PCB(R4),R0 ;
CLR 2(R0) ;Clear word 1 of process control block to reset nominal
; priority to 0.
SETPRI #0 ;Set the priority to 0
TST (SP)+ ;Flush old priority
CCC ;Clear condition code
RTS PC ;Done
.ENDC ; End of the ONMON material
;Force condition monitors. Data structures. TABOFS, FMBLK, MAKFORCE, DESFORCE
COMMENT ⊗ Certain tables are available via COMTAB entries. LERRPTR
points to the table ERRPTR of 16 words, one for each servo, which
points at the current error torques. LTHPTR points at the table THPTR
of 16 words, one for each servo, which points at the current joint
angles. ⊗
TABOFS:
COMMENT ⊗ R0 = Mechanism bit. Returns table offset (in bytes) in R0.
For example, if the mechanism is BARM, the OFBARM is returned. ⊗
BIT #YARM,R0 ;Is it this mechanism?
BEQ TABOF1 ;No
MOV #OFYARM,R0 ;Yes. Load up proper offset
RTS PC ; and return.
TABOF1: BIT #YHAND,R0 ;Is it this mechanism?
BEQ TABOF2 ;No
MOV #OFYHAND,R0 ;Yes. Load up proper offset
RTS PC ; and return.
TABOF2: BIT #BARM,R0 ;Is it this mechanism?
BEQ TABOF3 ;No
MOV #OFBARM,R0 ;Yes. Load up proper offset
RTS PC ; and return.
TABOF3: BIT #BHAND,R0 ;Is it this mechanism?
BEQ TABOF4 ;No
MOV #OFBHAND,R0 ;Yes. Load up proper offset
RTS PC ; and return.
TABOF4: HALERR TABMES ;Illegal
CLR R0 ;
RTS PC ;
TABMES: ASCIE </ILLEGAL MECHANISM/>
; Force monitor block (FMBLK)
II == 0
FMFOMO == II ;Force - moment array. 20 words.
;WORD 0,0 force component in X direction
;WORD 0,0 ditto for Y
;WORD 0,0 Z
;WORD 40200,0 (1.0) scaling factor, not used
;WORD 0,0 moment component in X direction
;WORD 0,0 Y
;WORD 0,0 Z
;WORD 40200,0 1.0
II == II + 40
FMRETO == II ;Reaction - torque array. 14 words.
II == II + 30
FMJOAN == II ;Joint angle array. 14 words.
II == II + 30
FMMECH == II ;Arm involved: mechanism bits
II == II + 2
FMSCAL == II ;Scale factor (sum of squares of RETO)
II == II + 4
FMMODE == II ;Mode bits
FMKIL == 2 ;set if this FM should go away.
FMBEX == 4 ;set if background job
; (fills reaction-torque array) exists
FMFEX == 10 ;set by GETFORCE, reset by MAKRT
II == II + 2
FMSIZ == II/2 ;Length in words of force monitor block
MAKFORCE: ;Interpreter routine
COMMENT ⊗ Prepares the force variable needed to compute forces. The
offset is the first argument, and the mechanism number is the second
argument. Sets the environment pointing to a new force monitor
block, whose force-moment array it fills from the two top elements of
the stack, which are then popped: the first is the force vector, the
second is the moment vector. These are both in hand coordinates. This
routine does not load the reaction-torque array or the joint angle
array. ⊗
MOV #FMSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new fmblock]
CLR FMMODE(R0) ;Reset all mode bits
MOV @IPC(R4),R1 ;R1 ← offset
BMPIPC ;Bump IPC
BIC #177400,R1 ;Remove level info.
ADD ENV(R4),R1 ;R1 ← LOC[place in environment]
MOV R0,(R1) ;Stow away the pointer to the new fmblock
MOV @IPC(R4),FMMECH(R0) ;Stow away the mechanism in the new fmblock
BMPIPC ;Bump IPC
MOV (R3)+,R1 ;R1 ← LOC[moment vector]
MOV (R3)+,R2 ;R2 ← LOC[force vector]
ADD #FMFOMO,R0 ;R0 ← LOC[force-moment vector]
MOV R3,-(SP) ;Save R3
MOV #6,R3 ;R3 ← count: how many words to transfer
MAKFC1: MOV (R2)+,(R0)+ ;transfer force vector
SOB R3,MAKFC1 ;repeat
MOV ONE,(R0)+ ;
CLR (R0)+ ;
MOV #6,R3 ;R3 ← count: how many words to transfer
MAKFC2: MOV (R1)+,(R0)+ ;transfer moment vector
SOB R3,MAKFC2 ;repeat
MOV ONE,(R0)+ ;
CLR (R0) ;
MOV (SP)+,R3 ;Restore R3
CCC ;Clear condition code
RTS PC ;Return
DESFORCE: ;Interpreter routine
COMMENT ⊗ One argument: the level-offset of the force block to
destroy. Reclaims the space. If anyone was using it, tough.
Currently nothing is done to inform anyone that it is going away. ⊗
MOV @IPC(R4),R0 ;R0 ← level-offset
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[environment point]
MOV R0,R2 ;For safekeeping
MOV (R0),R0 ;R0 ← LOC[fm control block]
BEQ DESF1 ;If any
CLR (R2) ;Remove mention in the environment
BIS #FMKIL,FMMODE(R0) ;Set the destroy bit.
CCC ;Clear condition code
RTS PC ;Done
DESF1: HALERR DESMSG ;Complain
SCC ;Set condition code
RTS PC ;Done
DESMSG: ASCIE </CANT DESTROY NON-EXISTENT FORCE MONITOR/>
; GETFORCE, MAKRT
GETFORCE: ;Interpreter routine
COMMENT ⊗ One argument, the level-offset of the force variable, which
points to the force monitor block. It is assumed that the reaction
torque array is already prepared. Calculates the current force on
the arm (it is a scalar) and places it on the stack. ⊗
MOV @IPC(R4),R0 ;R0 ← Level-offset
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← LOC[LOC[fmblock]]
MOV (R0),R2 ;R2 ← LOC[fmblock]
BEQ GTFRC4 ;If any
CLRF AC0 ;AC0 is the result force. Set to 0.
BIS #FMFEX,FMMODE(R2) ;Imply that we are still awake.
BIT #FMBEX,FMMODE(R2) ;Is there a MAKRT job?
BNE GTFRC6 ;Yes.
;Make a job for the MAKRT routine. Put LOC[fmblock] in its R0.
BIS #FMBEX,FMMODE(R2) ;Say that the MAKRT job exists.
MOV #210,R0 ;Room for process descriptor
JSR PC,GTFREE ;R0 ← LOC[new process descriptor]
MOV R2,PDBR0(R0) ;Put LOC[fmblock] in its new R0
MOV R0,PDBR1(R0) ;Put LOC[PCB] in its new R1
MOV #UFPUSE+UGRSAV,PDBSTA(R0);Use floating point, use saved registers.
MOV #420,UPDLEN(R0) ;Length of PCB
.IFNZ K2
MOV PCB(R4),R1 ;Copy the mapping stuff
MOV UIMAP(R1),UIMAP(R0) ;Map stuff
MOV UDMAP(R1),UDMAP(R0) ;Map stuff
.ENDC
;do something about the stack pointer
MOV #MAKRT,PDBPC(R0) ;Store away the new PC
FORK R0,#MAKRT,#3 ;Cause the new process to be started.
GTFRC6: MOV FMMECH(R2),R0 ;R0 ← mechanism
JSR PC,TABOFS ;R0 ← offset into joint error table
ADD LERRPTR,R0 ;R0 ← LOC[proper place in error torque]
MOV R2,R1 ;
ADD #FMRETO,R1 ;R1 ← LOC[reaction torque array]
MOV R3,-(SP) ;Save R3
BIT #AHAND,FMMECH(R2) ;Is it a hand?
BEQ GTFRC1 ;No
MOV #1,R3 ;Yes, R3 ← 2 ← count of joints
BR GTFRC2 ;
GTFRC1: MOV #6,R3 ;R3 ← 6 ← count of joints
GTFRC2: LDF (R1)+,AC1 ;AC1 ← reaction torque
MULF @(R0)+,AC1 ; * joint error
ADDF AC1,AC0 ;cumulate
SOB R3,GTFRC2 ;repeat
DIVF FMSCAL(R2),AC0 ;Normalise
MOV (SP)+,R3 ;Restore R3
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[new scalar block]
STF AC0,(R0) ;Store answer
CCC ;Clear condition code
RTS PC ;Return
GTFRC4: HALERR GTFMES ;Complain
SCC ;Set condition code
RTS PC ;Return
GTFMES: ASCIE </NO FORCE BLOCK/>
MAKRT:
COMMENT ⊗ This is a separate job which periodically reestablishes the
reaction torque array and the scale factor for a fmblock. When first
called, the location of the fmblock is in R0, and the location of the
PCB for the process is in R1. Makes sure that the force is still
needed (that is, that the FMKIL bit is off and the FMFEX is on) and
then sets up the array. Sleeps for half a second and tries it again.
If the FMKIL is on, then the fmblock and PCB are returned to free
storage and the process terminates. If FMFEX is off, then FMBEX is
turned off as well, the PCB is returned to free storage, and the
process terminates. ⊗
MOV R1,-(SP) ;Save the PCB address
MOV R0,R4 ;R4 ← LOC[fmblock]
MAKRT5: BIT #FMKIL,FMMODE(R4) ;Kill bit set?
BNE MAKRT3 ;Yes.
BIT #FMFEX,FMMODE(R4) ;Has GETFORCE been called recently?
BNE MAKRT4 ;Yes.
BIC #FMBEX,FMMODE(R4) ;No; say we are leaving.
BR MAKRT6 ;Leave
MAKRT4: BIC #FMFEX,FMMODE(R4) ;Reset the recency bit.
MOV FMMECH(R4),R0 ;R0 ← mechanism
JSR PC,TABOFS ;R0 ← offset into joint error table
ADD LTHPTR,R0 ;R0 ← LOC[proper place in joint ang table]
MOV R4,R1 ;
ADD #FMJOAN,R1 ;R1 ← LOC[joint angle list in fmblock]
BIT #AHAND,FMMECH(R4) ;Is it a hand?
BEQ MAKRT1 ;No
MOV #1,R3 ;Yes, R3 ← 1 ← words to transfer
BR MAKRT2 ;
MAKRT1: MOV #6,R3 ;R3 ← 6 ← words to transfer
MAKRT2: LDF @(R0)+,AC0 ;Transfer current joint angle
STF AC0,(R1)+ ;
SOB R3,MAKRT2 ;repeat
MOV R4,R0 ;
ADD #FMFOMO,R0 ;R0 ← LOC[force-moment array]
MOV R4,R1 ;
ADD #FMRETO,R1 ;R1 ← LOC[reaction torque array to be returned]
MOV FMMECH(R4),R3 ;R3 ← mechanism number
MOV R4,R2 ;
ADD #FMJOAN,R2 ;R2 ← LOC[current joint angles]
JSR PC,@LFORCE ;This actually fills the reaction torque array
MOV R4,R0 ;
ADD #FMRETO,R0 ;R0 ← LOC[reaction-torque array]
CLRF AC0 ;AC0 ← sum of the squares
BIT #AHAND,FMMECH(R4) ;Is it a hand?
BEQ MAKRT7 ;No
MOV #1,R3 ;Yes, R3 ← 1 ← words to sum
BR MAKRT8 ;
MAKRT7: MOV #6,R3 ;R3 ← 6 ← words to sum
MAKRT8: LDF (R0),AC1 ;compute sum of squares
MULF (R0)+,AC1 ;
ADDF AC1,AC0 ;
SOB R3,MAKRT8 ;
STF AC0,FMSCAL(R4) ;Store the sum of the squares
SLEEP #1000 ;Sleep half a second
BR MAKRT5 ;Do it again
MAKRT3: MOV R4,R0 ;R0 ← LOC[fmblock]
JSR PC,RLFREE ;Release the fmblock
MAKRT6: MOV (SP)+,R0 ;R0 ← LOC[PCB]
JSR PC,RLFREE ;Release the PCB
DISMIS ;Go away.
;Events: MAKEVT, SIGNAL, WAITE, DESEVT;
COMMENT ⊗ Events can be created (at the beginnings of blocks is the
usual place), signaled, awaited (in the middle of a block) and
destroyed (at the end of a block). Each event is a variable, that
is, it is refered to by a level-offset pair. However, its place in
the environment does not point to a graph node, since there is no
such thing as attachment to an event. The event itself is stored in
the environment. The garbage collector marking phase had better
understand this. ⊗
MAKEVT: ;Interpreter routine
COMMENT ⊗ A list of arguments, each of which is an offset. This list
is terminated by a zero entry. For each argument, a fresh event is
created and placed in the environment at the desired offset, current
level. ⊗
MOV @IPC(R4),R0 ;R0 ← offset
BIC #177400,R0 ;Get rid of level info.
BEQ MAKEV1 ;If none, done
BMPIPC ;Bump IPC
ADD ENV(R4),R0 ;R0 ← pointer into environment
EVMAK ;Make an event.
MOV (SP)+,(R0) ;Stuff it away.
BR MAKEVT ;Repeat
MAKEV1: BMPIPC ;Bump IPC
CCC ;Clear condition code.
RTS PC ;Done
SIGNAL: ;Interpreter routine. Signal the event of the level-offset pair.
MOV @IPC(R4),R0 ;R0 ← level-offset pair.
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← equivalent pointer into environment
EVSIG (R0) ;Signal that event.
CCC ;Clear condition code.
RTS PC ;Done
WAITE: ;Interpreter routine. Wait on the event of the level-offset pair.
MOV @IPC(R4),R0 ;R0 ← level-offset pair.
BMPIPC ;Bump IPC
JSR PC,GETARG ;R0 ← equivalent pointer into environment
EVWAIT (R0) ;Wait on that event.
BCS WAITE1 ;Return OK?
JMP TERMINATE ;The event was destroyed. I guess we should depart cleanly.
WAITE1:
CCC ;Clear condition code.
RTS PC ;Done
DESEVT: ;Interpreter routine
COMMENT ⊗ A list of arguments, each of which is an offset. This list
is terminated by a zero entry. For each argument, the event is
destroyed. ⊗
MOV @IPC(R4),R0 ;Get offset
BIC #177400,R0 ;Remove level info.
BEQ DESEV1 ;If none, done
BMPIPC ;Bump IPC
ADD ENV(R4),R0 ;R0 ← pointer into environment
EVKIL (R0) ;Kill the event
CLR (R0) ;Remove the event from the environment
BR DESEVT ;Repeat
DESEV1: BMPIPC ;Bump IPC
CCC ;Clear condition code.
RTS PC ;Done
;Output routines: PRINT, VALPRN, VARPRN, TYPVAL
PRINT: ;Interpreter routine
MOV @IPC(R4),R0 ;R0 ← Address of string
BMPIPC ;Bump IPC
EVWAIT CSLEVT ;
JSR PC,TYPSTR ;Type it out
EVSIG CSLEVT ;
CCC ;Clear condition code
RTS PC ;Done
VARPRN:
COMMENT ⊗ Interpreter routine. Prints the graph node pointed to by
the level-offset of the argument. ⊗
JSR PC,GTVAL ;Let GTVAL put value on stack
JMP VALPRN ;And let VALPRN take it from there.
VALPRN:
COMMENT ⊗ Interpreter routine. Prints the value the top of the stack
and pops it. ⊗
MOV (R3)+,R0 ;R0 ← LOC[value cell]
JSR PC,TYPVAL ;Go print it.
CCC ;Clear condition codes
RTS PC ;And return
TYPVAL:
COMMENT ⊗ R0 points to a value cell. Prints it according to its
type. Requires the floating package. ⊗
MOV R2,-(SP) ;Save R2
MOV R0,R2 ;R2 ← LOC[value cell]
EVWAIT CSLEVT ;
MOV #CRLFX,R0 ;CRLF
JSR PC,TYPSTR ;
MOVB TAGID(R2),R1
CMPB #SCLID,R1 ;A scalar?
BEQ TYPVL1 ;
CMPB #VCTID,R1 ;A vector?
BEQ TYPVL4 ;
CMPB #TRNID,R1 ;A trans?
BEQ TYPVL5 ;
TYPVL1: MOV #SNAME,R0 ;
JSR PC,TYPSTR ;"SCALAR "
MOV #OUTBUF,R0 ;
TYPVL2: LDF (R2),AC0 ;
JSR PC,CVG ;
MOV #OUTBUF,R0 ;
JSR PC,TYPSTR ;
TYPVL3: MOV #CRLFX,R0 ;CRLF
JSR PC,TYPSTR ;
EVSIG CSLEVT ;
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
TYPVL4: MOV #VNAME,R0 ;
JSR PC,TYPSTR ;"VECTOR "
MOV #OUTBUF,R0 ;
LDF (R2)+,AC0 ;
JSR PC,CVG ;
LDF (R2)+,AC0 ;
JSR PC,CVG ;
BR TYPVL2 ;Bum code for last field.
TYPVL5: MOV #TNAME,R0 ;
JSR PC,TYPSTR ;"TRANS "
MOV R3,-(SP) ;Save R3
MOV #4,R3 ;R3 ← Number of rows
TYPVL6: MOV #CRLFX,R0 ;
JSR PC,TYPSTR ;
MOV #OUTBUF,R0 ;
LDF (R2),AC0 ;
JSR PC,CVG ;
LDF 20(R2),AC0 ;
JSR PC,CVG ;
LDF 40(R2),AC0 ;
JSR PC,CVG ;
LDF 60(R2),AC0 ;
JSR PC,CVG ;
MOV #OUTBUF,R0 ;
JSR PC,TYPSTR ;
ADD #4,R2 ;Next row
SOB R3,TYPVL6 ;
MOV (SP)+,R3 ;Restore R3
BR TYPVL3 ;Go to the exit stage
SNAME: ASCIE /SCALAR /
VNAME: ASCIE /VECTOR /
TNAME: ASCIE /TRANS /
; BREAK, NOOP, TOPAL
.IFZ ALAID
BREAK: ;Interpreter routine
MOV #BRKMES,R0 ;
JSR PC,TYPSTR ;
BPT ;Cause a DDT break
CCC ;Clear condition code
RTS PC ;Done
BRKMES: ASCIE </
PROGRAM BREAK/>
.ENDC
NOOP: ;Interpreter routine
CCC ;Clear condition code
RTS PC ;Done
TOPAL: ;Interpreter routine
COMMENT ⊗ Escape to PAL. JSRs to the pseudo code. That code
should return via:
MOV PC,R0
RTS PC
⊗
JSR PC,@IPC(R4) ;Fly
ADD #2,R0 ;R0 ← Proper new IPC
MOV R0,IPC(R4) ;Hope R4, R3 still OK!
RTS PC ;Done.
;Initialization psops: PROG, ENDP, NOTICE
PROG:
COMMENT ⊗ Sets up the variables for each arm, with the associated
calculators. This is done by using some special-purpose pseudo-code
and setting this interpreter to work on it. There is one argument,
which is the version number of the pcode. ⊗
MOV IPC(R4),-(SP) ;Save the IPC.
MOV #PROGCD,IPC(R4) ;Set up a funny IPC
CALL INTERP ;Call ourselves to execute the code.
MOV (SP)+,IPC(R4) ;Restore the IPC
CCC ;Clear condition code
RTS PC ;Done
PROGCD:
XMVAR ;Make the mechanism variables
; YAOFST
; YHOFST
BAOFST
BHOFST
0
; XMEXP ;The expression for updating the YARM
; 0 ; no neededs (so not dependent on the mechanism)
; PCDYA ; code
; YACOFS ; offset of expression
; XMCLC ;Make it a calculator
; YACOFS ; offset of expression
; YAOFST ; offset of variable
; XMEXP ;The expression for updating the YHAND
; 0 ; no neededs (so not dependent on the mechanism)
; PCDYH ; code
; YHCOFS ; offset of expression
; XMCLC ;Make it a calculator
; YHCOFS ; offset of expression
; YHOFST ; offset of variable
XMEXP ;The expression for updating the BARM
0 ; no neededs (so not dependent on the mechanism)
PCDBA ; code
BACOFS ; offset of expression
XMCLC ;Make it a calculator
BACOFS ; offset of expression
BAOFST ; offset of variable
XMEXP ;The expression for updating the BHAND
0 ; no neededs (so not dependent on the mechanism)
PCDBH ; code
BHCOFS ; offset of expression
XMCLC ;Make it a calculator
BHCOFS ; offset of expression
BHOFST ; offset of variable
XPUSH ;Put some junk on the stack
0 ;
XENDCLC ;Returns to caller, and clears the stack
;PCDYA: XWHERE ;Expression for YARM
; YARM ;
; XENDCLC ;
;PCDYH: XWHERE ;Expression for YHAND
; YHAND ;
; XENDCLC ;
PCDBA: XWHERE ;Expression for BARM
BARM ;
XENDCLC ;
PCDBH: XWHERE ;Expression for BHAND
BHAND ;
XENDCLC ;
ENDP:
COMMENT ⊗ Cleans up the variables for each arm, with the associated
calculators. This is done by using some special-purpose pseudo-code
and setting this interpreter to work on it. ⊗
MOV IPC(R4),-(SP) ;Save the IPC.
MOV #ENDPCD,IPC(R4) ;Set up a funny IPC
CALL INTERP ;Call ourselves to execute the code.
MOV (SP)+,IPC(R4) ;Restore the IPC
CCC ;Clear condition code
RTS PC ;Done
ENDPCD:
XKVAR ;Kill the mechanism variables
; YAOFST
; YHOFST
BAOFST
BHOFST
0
XPUSH ;Put some junk on the stack
0 ;
XENDCLC ;Returns to caller, and clears the stack
NOTICE:
COMMENT ⊗ Called from the outside world, usually from DDT, after
someone has moved the arm and wants to make sure that the arm code
knows about it. Because of this special use, all registers are
saved. ⊗
MOV R0,-(SP) ;Save R0
MOV R1,-(SP) ;Save R1
STF AC0,-(SP) ;Save AC0
MOV #DVBKSZ,R0 ;Get a device block
JSR PC,GTFREE ;
MOV R0,R1 ;R1 ← LOC[device block]
MOV R0,-(SP) ;Save a copy to reclaim it later
MOV #NTCOF,R0 ;R0 ← LOC[coefficient list]
JSR PC,@LWHERE ;Get good values of the arm.
MOV (SP)+,R0 ;Reclaim the device block
JSR PC,RLFREE ;
LDF (SP)+,AC0 ;Restore AC0
MOV (SP)+,R1 ;Restore R1
MOV (SP)+,R0 ;Restore R0
RTS PC ;Done
NTCOF: BARM+BHAND ;Servo bit words
0
;BUGS
COMMENT ⊗
⊗